home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 17 / CU Amiga Magazine's Super CD-ROM 17 (1997)(EMAP Images)(GB)[!][issue 1997-12].iso / CUCD / Programming / DiceSource / src / vopts / command.c next >
C/C++ Source or Header  |  1997-09-09  |  30KB  |  1,007 lines

  1. /*
  2.  *    (c)Copyright 1992-1997 Obvious Implementations Corp.  Redistribution and
  3.  *    use is allowed under the terms of the DICE-LICENSE FILE,
  4.  *    DICE-LICENSE.TXT.
  5.  */
  6. #include "vopts.h"
  7.  
  8. Prototype void do_command(char *string);
  9. Prototype void read_file(int type);
  10. Prototype void save_file(int type);
  11. Prototype int get_filename(char *str);
  12. Prototype void reset_options(int reshow);
  13. Prototype void reset_group(struct G_OBJECT *object);
  14. Prototype int match_opt(char *optstr,char **valstr,char *buf, int exact);
  15. Prototype int set_option(struct G_OBJECT *object,char **argv, int exact);
  16. Prototype int parse_options(char *string);
  17. Prototype int get_option(struct G_OBJECT *object, char **buf, int *len);
  18. Prototype int append_option(char *optstr1, char *optstr2, char **buf, int *len);
  19.  
  20. parse_options(char *string);
  21.  
  22. /***********************************************************************************
  23.  * Procedure: do_command
  24.  * Synopsis:  do_command(string);
  25.  * Purpose:   Execute the command associated with a given string
  26.  ***********************************************************************************/
  27. void do_command(char *string)
  28. {
  29.    char *p;
  30.    char buf[10];
  31.    int len;
  32.    int type;
  33.  
  34.    /* The following valid commands are allowed: */
  35.    /*    READ  [ file | ? | ENV ]               */
  36.    /*    SAVE  [ file | ? | ENV ]               */
  37.    /*    RESET                                  */
  38.    /*    QUIT                                   */
  39.    /*    PARSE option                           */
  40.  
  41.    len = 0;
  42.    while (*string == ' ') string++;
  43.  
  44.    p = string;
  45.    while(((*p >= 'A') && (*p <= 'Z')) ||
  46.          ((*p >= 'a') && (*p <= 'z')))
  47.    {
  48.       if (*p < 'Z') /* Upper case characters which occur first in ASCII */
  49.          buf[len++] = *p;
  50.       else
  51.          buf[len++] = 'A' + (*p - 'a');
  52.       if (len > 5) break;
  53.       p++;
  54.    }
  55.    buf[len] = 0;
  56.    while(*p == ' ') p++;
  57.  
  58.    if (!strcmp(buf, "READ"))
  59.    {
  60.       global.fileop = 0;
  61.       type = get_filename(p);
  62.       if (type == 0)            /* user aborted filename                */
  63.          return;
  64.       SetWindowTitles(global.window, global.title, global.wtitle);
  65.       global.filetype = type;
  66.       read_file(type);
  67.    }
  68.    else if (!strcmp(buf, "SAVE"))
  69.    {
  70.       global.fileop = 1;
  71.       type = get_filename(p);
  72.       if (type == 0)            /* user aborted filename                */
  73.          return;
  74.       if (request(0, TEXT_SAVEOK, global.filename, NULL) == 0)
  75.          /* Chickened out...    */
  76.          return;
  77.       SetWindowTitles(global.window, global.title, global.wtitle);
  78.       global.filetype = type;
  79.       save_file(type);
  80.    }
  81.    else if (!strcmp(buf, "QUIT"))
  82.    {
  83.       global.done = 1;
  84.    }
  85.    else if (!strcmp(buf, "PARSE"))
  86.    {
  87.       char tbuf[MAX_STRING+1];
  88.       strncpy(tbuf, p, MAX_STRING);
  89.       set_gadgets(0);
  90.       parse_options(tbuf);
  91.       set_gadgets(1);
  92.    }
  93.    else if (!strcmp(buf, "RESET"))
  94.    {
  95.       reset_options(1);
  96.    }
  97.    else
  98.    {
  99.       /* We have an invalid option, ignore the command and let them know  */
  100.       /* about the problem with the option.                               */
  101.       request(1, TEXT_BADCMD, buf, NULL);
  102.       return;
  103.    }
  104. }
  105. /***********************************************************************************
  106.  * Procedure: read_file
  107.  * Synopsis:  read_file(type)
  108.  * Purpose:   read file, extract options according to (type) and display
  109.  ***********************************************************************************/
  110. void read_file(int type)
  111. {
  112.    char buf[256];
  113.  
  114.    if (type == FILE_ENV)
  115.    {
  116.       strncpy(buf, getenv("DCCOPTS"), 255);
  117.       reset_options(0);
  118.       parse_options(buf);
  119.    }
  120.    else
  121.    {
  122.       FILE *fp;
  123.       int cstate;
  124.  
  125.       fp = fopen(global.filename, "r");
  126.       if (fp == NULL)
  127.       {
  128.          request(1, TEXT_BADFILE, global.filename, NULL);
  129.          return;
  130.       }
  131.  
  132.       reset_options(0);
  133.       cstate = 1;
  134.       while(cstate && (fgets(buf, 256, fp) != NULL))
  135.       {
  136.          int len;
  137.          int extend;
  138.  
  139.          len = strlen(buf)-1;
  140.          if (len < 2) continue;
  141.  
  142.          buf[len] = 0; /* Wipe out the \n */
  143.          extend = 0;
  144.          if (buf[len-1] == '\\')
  145.          {
  146.             extend = 1;
  147.             buf[--len] = 0;
  148.          }
  149.  
  150.          switch(type)
  151.          {
  152.             case FILE_C:
  153.                /* Make sure it says #pragma DCCOPTS */
  154.                if (!memcmp(buf, "#pragma DCCOPTS ", 16))
  155.                {
  156.                   /* We actually got it */
  157.                   if (cstate == 3)
  158.                   {
  159.                      request(1, TEXT_EXTRAPRG, NULL, NULL);
  160.                      cstate = 0;
  161.                   }
  162.                   else
  163.                   {
  164.                      if (parse_options(buf+16))
  165.                         cstate = 0;
  166.                      else
  167.                         cstate = 2;
  168.                   }
  169.                }
  170.                else
  171.                {
  172.                   if (cstate == 2) cstate = 3;
  173.                }
  174.                break;
  175.             case FILE_OPTIONS:
  176.                cstate = !parse_options(buf);
  177.                break;
  178.             case FILE_DMAKEFILE:
  179.                if ((!memcmp(buf, "CFLAGS=", len=7)) ||
  180.                    (len = 0, cstate == 2))
  181.                {
  182.                   cstate = 0;
  183.                   if (extend) cstate = 2;
  184.                   if (parse_options(buf+len))
  185.                      cstate = 0;
  186.                }
  187.                break;
  188.          }
  189.       }
  190.    global.nameoffile = 1; /* indicate that global.filename has been read in */
  191.    fclose(fp);
  192.    }
  193.    set_gadgets(1);
  194. }
  195.  
  196. /***********************************************************************************
  197.  * Procedure: save_file
  198.  * Synopsis:  save_file(type)
  199.  * Purpose:   Write user selected options to file, formatted for type
  200.  ***********************************************************************************/
  201. void save_file(int type)
  202. {
  203. #define MAXOPTSTR 512
  204.  
  205.    char buf[MAXOPTSTR];
  206.    char *pbuf=buf;
  207.    struct G_GROUP *group;
  208.    int  ok, len = MAXOPTSTR - 1;
  209.    int  skipping = 0;
  210.  
  211.    *buf = 0;    /* just in case no options are set                      */
  212.  
  213.    if (ok = get_option(global.objects, &pbuf, &len)) /* didn't over run buffer...*/
  214.       for (group = global.groups;
  215.            ok  && (group != NULL);
  216.            group = (struct G_GROUP *)group->base.next)
  217.          {
  218.          if (!((type == FILE_ENV) && group->local))
  219.             ok = get_option(group->objects, &pbuf, &len);
  220.          }
  221.    if (!ok)
  222.       {
  223.       ok = request(0, TEXT_BIGOPTS, NULL, NULL);
  224.       if (!ok) return;
  225.       }
  226. #ifdef JGM_DBG
  227. printf("Options: %s\n", buf);
  228. #endif
  229.  
  230.    if (*buf == 0) return;       /* let's not try anything fancy         */
  231.  
  232.    if (type == FILE_ENV)
  233.    {
  234.       setenv("DCCOPTS", buf);
  235.    }
  236.    else
  237.    {
  238.  
  239.       FILE *fpout, *fpin;
  240.  
  241.       if (global.nameoffile != 1)
  242.       {
  243.          /* didn't already read this file - does it exist? */
  244.          fpin = fopen(global.filename, "r");
  245.          /* yes I know lock is more efficient ... */
  246.          if (fpin != NULL)
  247.          {
  248.             fclose(fpin); /* it don't have nothing we want */
  249.             ok = request(0, TEXT_OVWRITEOK, global.filename, NULL);
  250.             if (!ok) return;
  251.          }
  252.       }
  253.  
  254.       switch (type)
  255.       {
  256.          case FILE_OPTIONS:
  257.          {
  258.             char *p1 = buf, *p2;
  259.  
  260.             fpout = fopen(global.filename, "w");
  261.             if (fpout == NULL)
  262.             {
  263.                request(1, TEXT_BADFILE, global.filename, NULL);
  264.                return;
  265.             }
  266.  
  267.             do
  268.             {
  269.                p2 = strchr(p1+1, '-');
  270.                if (p2 != NULL)
  271.                   p2[-1] = '\0';
  272.                fputs(p1, fpout);
  273.                fputc('\n', fpout);
  274.                p1 = p2;
  275.             }
  276.             while (p1 != NULL);
  277.             fclose(fpout);
  278.             break;
  279.          }
  280.          case FILE_DMAKEFILE:
  281.          {
  282.             int werr = 1;       /* init to no error                     */
  283.             char newname[MAX_FILENAME+11];
  284.             char copybuf[256];
  285.  
  286.             strcpy(newname, global.filename);
  287.             strcat(newname, ".vopt-temp");
  288.             if ((fpin = fopen(newname, "r")) != NULL)
  289.             {
  290.                fclose(fpin);
  291.                remove(newname);
  292.             }
  293.             if (rename(global.filename, newname) < 0)   /* error        */
  294.             {
  295.                request(1, TEXT_NORENAME, global.filename, NULL);
  296.                return;
  297.             }
  298.             if ((fpin = fopen(newname, "r")) == NULL)
  299.             {
  300.                rename(newname, global.filename);
  301.                request(1, TEXT_NOREAD, global.filename, NULL);
  302.                return;
  303.             }
  304.             if ((fpout = fopen(global.filename, "w")) == NULL)
  305.             {
  306.                fclose(fpin);
  307.                rename(newname, global.filename);
  308.                request(1, TEXT_BADFILE, global.filename, NULL);
  309.                return;
  310.             }
  311.  
  312.             /* note that both fprintf() and fputs() return <0 for error */
  313.             while ((fgets(copybuf, 256, fpin) != NULL) && (werr >= 0))
  314.             {
  315.                if (!memcmp(copybuf, "CFLAGS=", 7))
  316.                {
  317.                   werr = fprintf(fpout, "%s%s\n", "CFLAGS= ", buf);
  318.                   skipping = 1;  /* might not turn out to be true... */                  
  319.                }
  320.                else 
  321.                   if (!skipping)
  322.                      werr = fputs(copybuf, fpout);
  323.                if (skipping)
  324.                {
  325.                   len = strlen(copybuf);
  326.                   if (copybuf[len-2] != '\\')
  327.                      skipping = 0; /* finished (if we ever started) */
  328.                }
  329.             }
  330.             fclose(fpout);
  331.             fclose(fpin);
  332.             if (werr < 0)       /* error writing file - don't rename    */
  333.             {
  334.                request(1, TEXT_NOWRITE, newname, NULL);
  335.                remove(global.filename);
  336.                rename(newname, global.filename);
  337.                return;
  338.             }
  339.             remove(newname); /*delete backup copy of file */
  340.             break;
  341.          }
  342.          case FILE_C:
  343.          {
  344.             int werr = 1;   /* init to no error                 */
  345.             int cstate = 0; /* -1 = #pragma DCCOPTS
  346.                                 0 = ordinary line
  347.                                 1 = comment continuation
  348.                             */
  349.             char newname[MAX_FILENAME+11];
  350.             char copybuf[256];
  351.  
  352.             strcpy(newname, global.filename);
  353.             strcat(newname, ".vopt-temp");
  354.             if ((fpin = fopen(newname, "r")) != NULL)
  355.             {
  356.                fclose(fpin);
  357.                remove(newname);
  358.             }
  359.             if (rename(global.filename, newname) < 0)   /* error        */
  360.             {
  361.                request(1, TEXT_NORENAME, global.filename, NULL);
  362.                return;
  363.             }
  364.             if ((fpin = fopen(newname, "r")) == NULL)
  365.             {
  366.                rename(newname, global.filename);
  367.                request(1, TEXT_NOOVWRITE, global.filename, NULL);
  368.                return;
  369.             }
  370.             if ((fpout = fopen(global.filename, "w")) == NULL)
  371.             {
  372.                fclose(fpin);
  373.                rename(newname, global.filename);
  374.                request(1, TEXT_BADFILE, global.filename, NULL);
  375.                return;
  376.             }
  377.  
  378.             werr = fprintf(fpout, "%s%s\n", "#pragma DCCOPTS ", buf);
  379.             while ((fgets(copybuf, 256, fpin) != NULL) && (werr >= 0))
  380.             {
  381.                if (cstate == 0)
  382.                {
  383.                   if (!memcmp(copybuf, "#pragma DCCOPTS ", 16))
  384.                      cstate = -1;
  385.                   else
  386.                   ;
  387.                }
  388.                /* else check for close comment                          */
  389.  
  390.                if (cstate >= 0)
  391.                   werr = fputs(copybuf, fpout);
  392.                else
  393.                   cstate = 0;   /* next line is ordinary - not comment  */
  394.             }
  395.  
  396.             fclose(fpout);
  397.             fclose(fpin);
  398.             if (werr < 0)       /* error writing file - don't rename    */
  399.             {
  400.                request(1, TEXT_NOOVWRITE, newname, NULL);
  401.                remove(global.filename);
  402.                rename(newname, global.filename);
  403.                return;
  404.             }
  405.             remove(newname); /*delete backup copy of file */
  406.             break;
  407.          }
  408.          default:
  409.          printf("unimplemented file type %d\n", type);
  410.       }
  411.    }
  412. }
  413.  
  414. /***********************************************************************************
  415.  * Procedure: get_filename
  416.  * Synopsis:  type = get_filename(str)
  417.  * Purpose:   Parse out a file descriptor and return a type for that name
  418.  ***********************************************************************************/
  419. int get_filename(char *str)
  420. {
  421.    char *p;
  422.    int len;
  423.  
  424.    if (*str)
  425.    {
  426.       /* They actually specified a name.  All we need to do is replace the */
  427.       /* current one.  If they specified a '?', then we need to prompt the */
  428.       /* user with the file requester.  We will assume the ASL one for now */
  429.       /* but should have a reasonable fall back one.                       */
  430.       if (!strcmp(str, "?"))
  431.       {
  432.  
  433.          if (global.freq) /* could be ASL or Arp */
  434.          {
  435.             int n;
  436.             if (AslBase != NULL)
  437.             {
  438. /*
  439.                struct TagItem taglist[2];
  440. */
  441.                struct TagItem taglist[5];
  442. #if 0
  443.                taglist[0].ti_Tag = ASL_Hail;
  444.                taglist[0].ti_Data = (ULONG)(v+1);
  445. #endif
  446. /* new */
  447.                /* Pattern to match against project files */
  448.                taglist[0].ti_Tag  = ASL_Pattern;
  449.                taglist[0].ti_Data = (ULONG)"(#?FILE|#?DM#AK#?|#?.DICE)";
  450.  
  451.                taglist[1].ti_Tag  = ASL_File;
  452.                taglist[1].ti_Data = (ULONG)"";
  453.  
  454.                taglist[2].ti_Tag  = ASL_Dir;
  455.                taglist[2].ti_Data = (ULONG)"";
  456.  
  457.  
  458.                taglist[3].ti_Tag = TAG_DONE;
  459.    
  460.                if (!AslRequest( (APTR)global.freq, taglist))
  461.                   return(0);
  462.             }
  463.             else /* must be Arp since we have a requester... */
  464.             {
  465.                if (!ArpFileRequest(global.freq))
  466.                   return (0);
  467.             }   
  468.             n = strlen(global.freq->rf_Dir);
  469.             if (n > 255) n = 255;
  470.             p = global.filename;
  471.             memcpy(p, global.freq->rf_Dir, n);
  472.             p += n;
  473.  
  474.             if ((n > 0) && (p[-1] != ':') && (p[-1] != '/'))
  475.             {
  476.                *p++ = '/';
  477.                n++;
  478.             }
  479.             len = strlen(global.freq->rf_File);
  480.             if ((n + len) > 255) len = 255-n;
  481.             strncpy(p, global.freq->rf_File, len);
  482.             p[len] = 0;
  483.  
  484.             global.nameoffile = 0; /* new name - possibly unsafe to write */
  485.          }
  486.          else
  487.          {
  488.             /* We don't have ASL.LIBRARY, try something else instead */
  489.             set_fr_gadgets(global.filename);
  490.             /* If user hits save button, we'll be back with the name */
  491.             return 0;
  492.  
  493.          }
  494.       }
  495.       else
  496.       {
  497.          strncpy(global.filename, str, MAX_FILENAME);
  498.       }
  499.    }
  500.  
  501.    if (!stricmp(global.filename, "ENV"))
  502.    {
  503.       strcpy(global.title, global.text[TEXT_GLOBOPTS]);
  504.       return(FILE_ENV);
  505.    }
  506.  
  507.    len = strlen(global.filename);
  508.    p = global.filename + strlen(global.filename);
  509.  
  510.    strcpy(global.title, global.text[TEXT_FILEOPTS]);
  511.    strncat(global.title, global.filename, MAX_TITLE);
  512.    global.title[MAX_TITLE] = 0;
  513.  
  514.    if (len < 2)
  515.       return(FILE_OPTIONS);
  516.  
  517.    if ((len >= 7) && (!stricmp(p-7, "DCCOPTS")))
  518.       return(FILE_OPTIONS);
  519.  
  520.    if ((len >= 9) && (!stricmp(p-9, "DMAKEFILE")))
  521.       return(FILE_DMAKEFILE);
  522.  
  523.    if ((len >= 5) && (!stricmp(p-5, ".DICE")))
  524.       return(FILE_DMAKEFILE);
  525.  
  526.    if (!stricmp(p-2, ".H"))
  527.       return(FILE_C);
  528.  
  529.    if (!stricmp(p-2, ".C"))
  530.       return(FILE_C);
  531.  
  532.    return(FILE_OPTIONS);
  533. }
  534.  
  535. /***********************************************************************************
  536.  * Procedure: reset_options
  537.  * Synopsis:  reset_options(reshow)
  538.  * Purpose:   Reset all the options to the default values
  539.  ***********************************************************************************/
  540. void reset_options(int reshow)
  541. {
  542.    struct G_GROUP *group;
  543.  
  544.    set_gadgets(0);
  545.  
  546.    reset_group(global.objects);
  547.    for(group = global.groups; group != NULL;
  548.        group = (struct G_GROUP *)group->base.next)
  549.    {
  550.       reset_group(group->objects);
  551.    }
  552.    if (reshow) set_gadgets(1);
  553. }
  554.  
  555. /***********************************************************************************
  556.  * Procedure: reset_group
  557.  * Synopsis:  reset_group(object)
  558.  * Purpose:   Reset all the options in a group to the default values
  559.  ***********************************************************************************/
  560. void reset_group(struct G_OBJECT *object)
  561. {
  562. #define object_check ((struct G_CHECK *)object)
  563. #define object_list  ((struct G_LIST *)object)
  564. #define object_str   ((struct G_STRING *)object)
  565. #define object_cycle ((struct G_CYCLE *)object)
  566.  
  567.    while(object != NULL)
  568.    {
  569.       switch(object->class)
  570.       {
  571.          case CLASS_STRING:
  572.             object_str->buf[0] = 0;
  573.             break;
  574.          case CLASS_CYCLE:
  575.             {
  576.                struct G_VALUE *val;
  577.  
  578.                val = object_cycle->curval = object_cycle->values;
  579.                while(val != NULL)
  580.                {
  581.                   if (val->string)
  582.                      val->string->buf[0] = 0;
  583.                   val = val->next;
  584.                }
  585.             }
  586.             break;
  587.          case CLASS_LIST:
  588.             {
  589.                struct G_ENTRY *ent;
  590.                ent = object_list->first;
  591.                object_list->first = object_list->top = NULL;
  592.                object_list->string = object_list->maxent = 0;
  593.                while(ent != NULL)
  594.                {
  595.                   struct G_ENTRY *nextent;
  596.  
  597.                   nextent = (struct G_ENTRY *)ent->base.next;
  598.                   free_mem(ent, sizeof(struct G_ENTRY));
  599.                   ent = nextent;
  600.                }
  601.             }
  602.             break;
  603.          case CLASS_CHECK:
  604.             object->state = 0;
  605.             if (!object_check->option1[0]) object->state = 1;
  606.             break;
  607.          default:
  608.             object->state = 0;
  609.             break;
  610.       }
  611.       object = object->next;
  612.    }
  613. #undef object_list
  614. #undef object_str
  615. #undef object_cycle
  616. #undef object_check
  617. }
  618. /***********************************************************************************
  619.  * Procedure: match_opt
  620.  * Synopsis:  rc = match_opt(optstr, argv, buf, exact)
  621.  * Purpose:   Compare a string to see if it matches a given option
  622.  *            If a successful match, return the number of argv entries used.
  623.  *            If exact is specified, do not look for extension strings.
  624.  ***********************************************************************************/
  625. int match_opt(char *optstr,
  626.               char **argv,
  627.               char *buf,
  628.               int exact
  629.              )
  630. {
  631.    char *p;
  632.    int len, olen;
  633.  
  634.    p = strchr(optstr, '%');
  635.    if ((p == NULL) || (buf == NULL))
  636.    {
  637.       /* We have just a plain option.  For it to match, we must have */
  638.       /* a complete match on the characters.                         */
  639.       return(!strcmp(optstr, *argv));
  640.    }
  641.  
  642.    if (exact) return 0; /* not looking for options strings this pass    */
  643.  
  644.    /* Not a plain one.  There are a couple of possibilities:       */
  645.    /*  -opt%s       No space allowed after the option              */
  646.    /*  -opt %s      Space after option is optional                 */
  647.    /*  -opt  %s     Space after option is mandatory.               */
  648.    olen = len = p - optstr;
  649.    while(len && (optstr[len-1] == ' ')) len--;
  650.    if (memcmp(optstr, *argv, len))
  651.       return(0);
  652.  
  653.    /* We know that at least we match the start of the option.  Now we */
  654.    /* Have to get the buffer filled in.                               */
  655.    if ((*argv)[len]) /* We know that the parameter is with this option */
  656.    {
  657.       /* If the space is mandatory, we obviously didn't make a match  */
  658.       if ((olen - len) > 1) return(0);
  659.       strncpy(buf, argv[0]+len, MAX_STRING);
  660.       return(1);
  661.    }
  662.  
  663.    /* Parameter is with the next option */
  664.    /* If the space is not allowed, we didn't get a match */
  665.    if ((olen == len) || (argv[1] == NULL)) return(0);
  666.    strncpy(buf, argv[1], MAX_STRING);
  667.    return(2);
  668. }
  669.  
  670. /***********************************************************************************
  671.  * Procedure: append_option
  672.  * Synopsis:  rc = append_option(optstr, buffer, length)
  673.  * Purpose:   append option to string buffer, update count and pointer
  674.  ***********************************************************************************/
  675. int append_option(char *optstr1, char *optstr2, char **buf, int *len)
  676. {
  677.    int l;
  678.    char *p, *optr;
  679.  
  680.    optr = optstr1;      /* unless we change it to fetch a string        */
  681.    p = strchr(optstr1, '%');
  682.    if ((p != NULL) && (optstr2 != NULL)) /* seems we have a string      */
  683.    {
  684.       l = p - optstr1;
  685.       if (l >= *len) return 0;  /*no room to save it                    */
  686.       if (optstr1[l-1] == ' ')
  687.          l -= 1;
  688.       memcpy(*buf, optstr1, l);
  689.       *buf += l;
  690.       *len -= l;
  691.       **buf = '\0';     /* just in case next step fails...              */
  692.       optr = optstr2;
  693.    }
  694.  
  695.    /* now copy whatever is left and put a blank on the end              */
  696.    l = strlen(optr);
  697.    if (l >= *len) return 0;     /* no room to save it                   */
  698.    strcpy(*buf, optr);
  699.    *buf += l;
  700.    strcpy(*buf, " ");
  701.    *buf += 1;
  702.    *len -= (l + 1);
  703.  
  704.    return 1;
  705. }
  706.  
  707. /***********************************************************************************
  708.  * Procedure: get_option
  709.  * Synopsis:  rc = get_option(object, buffer, length)
  710.  * Purpose:   make a string of all options that are set
  711.  ***********************************************************************************/
  712. int get_option(struct G_OBJECT *object,
  713.                char **buf,
  714.                int *len
  715.               )
  716. {
  717. #define object_list  ((struct G_LIST   *)object)
  718. #define object_str   ((struct G_STRING *)object)
  719. #define object_check ((struct G_CHECK  *)object)
  720. #define object_cycle ((struct G_CYCLE  *)object)
  721.    while (object != NULL)
  722.    {
  723.       switch (object->class)
  724.       {
  725.          case CLASS_CYCLE:
  726.          {
  727.             struct G_VALUE *val;
  728.  
  729.             val = object_cycle->curval;
  730.             if (val->option[0] == 0) break;     /* option not set       */
  731.             if
  732.             (
  733.                 append_option(val->option,
  734.                               val->string != NULL ? val->string->buf : NULL,
  735.                               buf, len)
  736.                 == 0
  737.             )
  738.                return 0;        /* no room to save it                   */
  739.             break;
  740.          }
  741.          case CLASS_CHECK:
  742.          {
  743.             char *p;
  744.             p = object->state ? object_check->option1 : object_check->option0;
  745.             if (*p)             /* do we gots a string?                 */
  746.                if (append_option(p, NULL, buf, len) == 0)
  747.                   return 0;     /* shucks, nowhere to put it            */
  748.             break;
  749.          }
  750.          case CLASS_STRING:
  751.          {
  752.             if (object_str->buf[0] == 0) break; /* empty string         */
  753.             if
  754.             (
  755.                 append_option(object_str->option,
  756.                               object_str->buf,
  757.                               buf, len)
  758.                 == 0
  759.             )
  760.                return 0;        /* no room, buf is full...              */
  761.             break;
  762.          }
  763.          case CLASS_LIST:
  764.          {
  765.             struct G_ENTRY *ent;
  766.  
  767.             ent = object_list->first;
  768.             while (ent != NULL) /* empty list will fall through         */
  769.             {
  770.                if
  771.                (append_option(object_list->option, ent->buf, buf, len) == 0)
  772.                   return 0;     /* seem familiar?                       */
  773.                ent = (struct G_ENTRY *)ent->base.next;
  774.             }
  775.             break;
  776.          }
  777.       }
  778.       object = object->next;
  779.    }
  780.    return 1;
  781. #undef object_check
  782. #undef object_list
  783. #undef object_str
  784. #undef object_cycle
  785. }
  786.  
  787. /***********************************************************************************
  788.  * Procedure: set_option
  789.  * Synopsis:  rc = set_option(object, optstr, exact)
  790.  * Purpose:   Set an option based on a option string, allowing parms if not exact
  791.  ***********************************************************************************/
  792. int set_option(struct G_OBJECT *object,
  793.                char **argv,
  794.                int exact
  795.               )
  796. {
  797.    int used;
  798. #define object_list  ((struct G_LIST   *)object)
  799. #define object_str   ((struct G_STRING *)object)
  800. #define object_check ((struct G_CHECK  *)object)
  801. #define object_cycle ((struct G_CYCLE  *)object)
  802.  
  803.    used = 0;
  804.  
  805.    while((used == 0) && (object != NULL))
  806.    {
  807.       switch(object->class)
  808.       {
  809.          case CLASS_STRING:
  810.             used = match_opt(object_str->option, argv, object_str->buf, exact);
  811.             break;
  812.          case CLASS_CYCLE:
  813.             {
  814.                struct G_VALUE *val;
  815.  
  816.                for(val = object_cycle->values;
  817.                    val != NULL;
  818.                    val = (struct G_VALUE *)val->next)
  819.                {
  820.                   used = match_opt(val->option, argv,
  821.                                    val->string ? val->string->buf : NULL,
  822.                                    exact);
  823.                   if (used)
  824.                   {
  825.                      object_cycle->curval = val;
  826.                      break;
  827.                   }
  828.                }
  829.             }
  830.             break;
  831.          case CLASS_CHECK:
  832.             used = match_opt(object_check->option0, argv, NULL, exact);
  833.             if (used)
  834.             {
  835.                object->state = 0;
  836.             }
  837.             else
  838.             {
  839.                used = match_opt(object_check->option1, argv, NULL, exact);
  840.                if (used)
  841.                {
  842.                   object->state = 1;
  843.                }
  844.             }
  845.             break;
  846.          case CLASS_LIST:
  847.             {
  848.                char buf[MAX_STRING+1];
  849.  
  850.                used = match_opt(object_list->option, argv, buf, exact);
  851.                if (used)
  852.                {
  853.                   struct G_ENTRY *ent, *prevent;
  854.  
  855.                   ent = get_mem(sizeof(struct G_ENTRY));
  856.                   if (ent)
  857.                   {
  858.                      strcpy(ent->buf, buf);
  859.                      if ((prevent = object_list->top) == NULL)
  860.                         object_list->top = object_list->first = ent;
  861.                      else
  862.                      {
  863.                         while(prevent->base.next)
  864.                            prevent = (struct G_ENTRY *)prevent->base.next;
  865.                         prevent->base.next = (struct G_OBJECT *)ent;
  866.                         ent->base.prev = (struct G_OBJECT *)prevent;
  867.                      }
  868.                   }
  869.                }
  870.             }
  871.             break;
  872.          default:
  873.             object->state = 0;
  874.             break;
  875.       }
  876.       object = object->next;
  877.    }
  878.  
  879.    return(used);
  880. #undef object_check
  881. #undef object_list
  882. #undef object_str
  883. #undef object_cycle
  884. }
  885.  
  886. /***********************************************************************************
  887.  * Procedure: parse_options
  888.  * Synopsis:  parse_options(string);
  889.  * Purpose:   Parse an option and set the appropriate options in the global options
  890.  *            data.  This subroutine destroys the string buffer.
  891.  ***********************************************************************************/
  892. #define MAX_BUF 100
  893.  
  894. int parse_options(char *string)
  895. {
  896. #define MAX_ARGS 80
  897.  
  898.    /* First we need to create an ARGV type array */
  899.    /* We will assume no more than 80 arguments in a line */
  900.    char *argv[MAX_ARGS];
  901.    int argc;
  902.    int state;
  903. /*
  904.  * Character Classes:
  905.  *   SQ    '           Single Quote
  906.  *   DQ    "           Double Quote
  907.  *   BL    Space, Tab  White Space
  908.  *   NL    Null        End of the string
  909.  *   OT    Other       Anything else
  910.  *        SQ   DQ   BL   NL   OT
  911.  *    0    1    2    0    x    3
  912.  *    1   >0   +1   +1   >x   +1
  913.  *    2   +2   >0   +2   >x   +2
  914.  *    3   +3   +3   >0   >x   +3
  915.  */
  916. #define SQ 0
  917. #define DQ 1
  918. #define BL 2
  919. #define NL 3
  920. #define OT 4
  921. #define EXI 15
  922. #define STATE_MASK 0x0f
  923. #define ACTION_SHIFT  4
  924. #define ACT_SKP 1
  925. #define ACT_OUT 2
  926. #define ACT_BEG 3
  927. #define SKP (ACT_SKP << ACTION_SHIFT)
  928. #define OUT (ACT_OUT << ACTION_SHIFT)
  929. #define BEG (ACT_BEG << ACTION_SHIFT)
  930.  
  931.    static char class_tab[][5] = {
  932.       { SKP|1, SKP|2,     0,       EXI, BEG|3 },
  933.       { OUT|0,     1,     1,   OUT|EXI,     1 },
  934.       {     2, OUT|0,     2,   OUT|EXI,     2 },
  935.       {     3,     3, OUT|0,   OUT|EXI,     3 }
  936.    };
  937.  
  938.    argc = 0;
  939.    state = 0;
  940.    while(state != EXI)
  941.    {
  942.       int class;
  943.       int look;
  944.  
  945.       switch(*string)
  946.       {
  947.          case '\'': class = SQ; break;
  948.          case '"' : class = DQ; break;
  949.          case ' ' :
  950.          case '\t': class = BL; break;
  951.          case    0: class = NL; break;
  952.          default:   class = OT; break;
  953.       }
  954.       look = class_tab[state][class];
  955.       state = look & STATE_MASK;
  956.       switch(look >> ACTION_SHIFT)
  957.       {
  958.          case ACT_SKP:
  959.             argv[argc++] = string+1;
  960.             break;
  961.          case ACT_OUT:
  962.             *string = 0;
  963.             break;
  964.          case ACT_BEG:
  965.             argv[argc++] = string;
  966.             break;
  967.       }
  968.       string++;
  969.    }
  970.  
  971.    if (argc == 0) return(0);
  972.  
  973.    argv[argc] = NULL;
  974.  
  975.    {
  976.       int i, used, exact;
  977.       struct G_GROUP *group;
  978.  
  979.       for(i = 0; i < argc; i += used)
  980.       {
  981.  
  982.          used = 0;      /* we've used what we used before       */
  983.          /* do exact matches first to find things like -I0      */
  984.          for (exact = 1; (exact >= 0) && (used == 0); exact -= 1)
  985.          {
  986.             used = set_option(global.objects, argv+i, exact);
  987.  
  988.             for(group = global.groups; (used == 0) && (group != NULL);
  989.                 group = (struct G_GROUP *)group->base.next)
  990.             {
  991.                used = set_option(group->objects, argv+i, exact);
  992.             }
  993.          }
  994.          if ((!used) && (exact <= 0))
  995.          {
  996.             /* The option wasn't found, tell them in some way */
  997.             if (!request(0, TEXT_BADOPT, argv[i], NULL))
  998.                return(1);
  999.             else
  1000.                used = 1; /* throw away the token and continue */
  1001.          }
  1002.  
  1003.       }
  1004.    }
  1005.    return(0);
  1006. }
  1007.